package com.framework.view.indicator;

import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.widget.ViewPager2;

import com.framework.view.indicator.configs.AbstractTitleConfig;
import com.framework.view.indicator.configs.GlobalConfig;
import com.framework.view.indicator.configs.LayoutConfig;
import com.framework.view.indicator.configs.MidDividerConfig;
import com.framework.view.indicator.configs.TextTitleConfig;
import com.framework.view.indicator.indicators.LineIndicatorView;
import com.framework.view.indicator.indicators.PointIndicatorView;
import com.framework.view.indicator.indicators.WrapIndicatorView;
import com.framework.view.indicator.indicators.configs.AbstractIndicatorConfig;
import com.framework.view.indicator.indicators.configs.LineIndicatorConfig;
import com.framework.view.indicator.indicators.configs.PointIndicatorConfig;
import com.framework.view.indicator.indicators.configs.WrapIndicatorConfig;
import com.framework.view.indicator.listener.OnItemClickListener;
import com.framework.view.indicator.listener.OnSimpleScrollResultListener;
import com.framework.view.indicator.model.PositionInfo;
import com.framework.view.indicator.model.ScrollState;

import java.util.ArrayList;
import java.util.List;

/**
 * 万能的上帝指示器
 *
 * @author liangzx
 * @version 1.0
 * @time 2019-12-14 18:23
 **/
public final class GodIndicator extends FrameLayout implements OnPageChangedListener, OnScrollResultListenerForLib, View.OnClickListener {

    BaseIndicatorAdapter mAdapter;
    CalculationHelper mHelper;
    /**
     * 全局配置
     */
    GlobalConfig mGlobalConfig;
    /**
     * 当非自适应模式会初始化该容器，用于包裹全部控件，可横向滚动
     */
    HorizontalScrollView mScrollView;
    /**
     * 内容容器，包含标题和指示器
     */
    FrameLayout mContentContainer;
    /**
     * 标题容器，包含所有标题
     */
    LinearLayout mTitleContainer;
    /**
     * 指示器容器
     */
    IPageIndicator mIPageIndicator;
    /**
     * 点击事件监听
     */
    OnTitleClickListener mOnTitleClickListener = new OnTitleClickListener() {
        @Override
        public void onTitleClick(int position) {
            if (null == mAdapter.mViewPager) {
                return;
            }
            if (mAdapter.mViewPager instanceof ViewPager) {
                ((ViewPager) mAdapter.mViewPager).setCurrentItem(position);
            }
            if (mAdapter.mViewPager instanceof ViewPager2) {
                ((ViewPager2) mAdapter.mViewPager).setCurrentItem(position);
            }
        }
    };
    /**
     * 标题坐标信息列表
     */
    List<PositionInfo> mTitlePositions;

    public GodIndicator(@NonNull Context context) {
        super(context);
    }

    public GodIndicator(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public GodIndicator(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onClick(View view) {
        int position = (int) view.getTag();
        if (null != mAdapter) {
            if (null != mAdapter.getListener()
                    && mAdapter.getListener() instanceof OnSimpleScrollResultListener) {
                ((OnSimpleScrollResultListener) mAdapter.getListener()).onTitleClick(position);
            }
            if (mAdapter instanceof LayoutIndicatorAdapter) {
                final LayoutIndicatorAdapter layoutIndicatorAdapter = (LayoutIndicatorAdapter) mAdapter;
                final OnItemClickListener listener = layoutIndicatorAdapter.getOnItemClickListener();
                if (null != listener) {
                    listener.onItemClick(layoutIndicatorAdapter, view, view, position);
                }
            }
        }
        if (null != mOnTitleClickListener) {
            mOnTitleClickListener.onTitleClick(position);
        }
    }

    /**
     * 设置适配器
     *
     * @param adapter
     */
    public void setAdapter(BaseIndicatorAdapter adapter) {
        if (mAdapter == adapter) {
            return;
        }
        mAdapter = adapter;
        mAdapter.registerDataSetObserver(mDataSetObserver);
        init();
        globalListenerAndBindVp();
    }

    /**
     * 数据变化通知接收
     */
    private DataSetObserver mDataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            init();
        }

        @Override
        public void onInvalidated() {
            init();
            globalListenerAndBindVp();
        }
    };

    /**
     * 初始化控件总入口
     */
    private void init() {
        removeAllViews();
        initGlobalConfig();
        if (null != mAdapter && mAdapter.getCount() > 0) {
            initContainter();
            initTitleViews();
            initIndicatorView();
        }
    }

    /**
     * 初始化全局配置
     */
    private void initGlobalConfig() {
        mGlobalConfig = null;
        if (null == mAdapter) {
            return;
        }
        mGlobalConfig = mAdapter.getGlobalConfig();
        if (null == mGlobalConfig) {
            mGlobalConfig = GlobalConfig.factory();
        }
        if (null == mHelper) {
            mHelper = CalculationHelper.factory(this);
        }
        mHelper.setSkimOver(mGlobalConfig.isFlit());
        mHelper.setTotalCount(mAdapter.getCount());
        setPadding(mGlobalConfig.getPendingLeft(), 0, mGlobalConfig.getPendingRight(), 0);
    }

    /**
     * 初始化容器
     */
    private void initContainter() {
        //是否可以超出屏幕
        final boolean canOffScreen = mGlobalConfig.isCanOffScreen();
        if (canOffScreen) {
            mScrollView = new HorizontalScrollView(getContext());
            mScrollView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            mScrollView.setFillViewport(true);
            mScrollView.setHorizontalScrollBarEnabled(false);
            addView(mScrollView);
        }

        mContentContainer = new FrameLayout(getContext());
        if (canOffScreen) {
            mContentContainer.setLayoutParams(new HorizontalScrollView.LayoutParams(
                    HorizontalScrollView.LayoutParams.WRAP_CONTENT
                    , HorizontalScrollView.LayoutParams.MATCH_PARENT));
            mScrollView.addView(mContentContainer);
        } else {
            mContentContainer.setLayoutParams(new LayoutParams(
                    LayoutParams.MATCH_PARENT
                    , LayoutParams.MATCH_PARENT));
            addView(mContentContainer);
        }
    }

    /**
     * 初始化标题
     */
    private void initTitleViews() {
        //是否可以超出屏幕
        final boolean canOffScreen = mGlobalConfig.isCanOffScreen();
        if (mAdapter instanceof GodIndicatorAdapter) {
            GodIndicatorAdapter adapter = (GodIndicatorAdapter) mAdapter;
            AbstractTitleConfig titleConfig = adapter.getTitleConfig();
            MidDividerConfig midDividerConfig = titleConfig.getMidDividerConfig();
            mHasMidDividerView = null != midDividerConfig && midDividerConfig.hasConfig();
            mTitleContainer = new LinearLayout(getContext());
            mTitleContainer.setOrientation(LinearLayout.HORIZONTAL);
            if (canOffScreen) {
                mTitleContainer.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, null == titleConfig ? 0 : LayoutParams.MATCH_PARENT));
            } else {
                mTitleContainer.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, null == titleConfig ? 0 : LayoutParams.MATCH_PARENT));
            }
            mContentContainer.addView(mTitleContainer);
            int totalCount = adapter.getCount();
            LinearLayout.LayoutParams itemParams;
            itemParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, null == titleConfig ? 0 : LayoutParams.MATCH_PARENT);
            itemParams.weight = 1;
            if (titleConfig instanceof TextTitleConfig) {
                adapter.removeBadgeChangeListeners();
                for (int i = 0; i < totalCount; i++) {
                    TextTitleView item = new TextTitleView(getContext());
                    item.setLayoutParams(itemParams);
                    item.init((GodIndicatorAdapter) mAdapter, i);
                    item.setTag(i);
                    item.setOnClickListener(this);
                    adapter.addBadgeChangeListener(item);
                    mTitleContainer.addView(item);
                    if (mHasMidDividerView && i < totalCount - 1) {
                        mTitleContainer.addView(midDividerView(midDividerConfig));
                    }
                }
            } else {
                for (int i = 0; i < totalCount; i++) {
                    View item = new View(getContext());
                    item.setLayoutParams(itemParams);
                    mTitleContainer.addView(item);
                    if (mHasMidDividerView && i < totalCount - 1) {
                        mTitleContainer.addView(midDividerView(midDividerConfig));
                    }
                }
            }
        } else if (mAdapter instanceof LayoutIndicatorAdapter) {
            LayoutIndicatorAdapter adapter = (LayoutIndicatorAdapter) mAdapter;
            LayoutConfig layoutConfig = adapter.getTitleConfig();
            MidDividerConfig midDividerConfig = layoutConfig.getMidDividerConfig();
            mHasMidDividerView = null != midDividerConfig && midDividerConfig.hasConfig();
            mTitleContainer = new LinearLayout(getContext());
            if (canOffScreen) {
                mTitleContainer.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, null == layoutConfig ? 0 : LayoutParams.MATCH_PARENT));
            } else {
                mTitleContainer.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, null == layoutConfig ? 0 : LayoutParams.MATCH_PARENT));
            }
            mContentContainer.addView(mTitleContainer);
            int totalCount = adapter.getCount();
            LinearLayout.LayoutParams itemParams;
            if (canOffScreen) {
                itemParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, null == layoutConfig ? 0 : LayoutParams.MATCH_PARENT);
            } else {
                itemParams = new LinearLayout.LayoutParams(0, null == layoutConfig ? 0 : LayoutParams.MATCH_PARENT);
            }
            itemParams.weight = 1;
            for (int i = 0; i < totalCount; i++) {
                CustomLayoutTitleView item = new CustomLayoutTitleView(getContext());
                item.setLayoutParams(itemParams);
                item.init((LayoutIndicatorAdapter) mAdapter, i);
                item.setOnChildClickListener(mOnTitleClickListener);
                item.setTag(i);
                item.setOnClickListener(this);
                mTitleContainer.addView(item);
                if (mHasMidDividerView && i < totalCount - 1) {
                    mTitleContainer.addView(midDividerView(midDividerConfig));
                }
            }
        }
    }

    //是否有中间分隔线
    private boolean mHasMidDividerView = true;

    /**
     * 中间分隔线
     */
    private View midDividerView(MidDividerConfig config) {
        View divider = new View(getContext());
        divider.setLayoutParams(config.getLayoutParams());
        divider.setBackground(config.getDrawable());
        divider.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        return divider;
    }

    /**
     * 初始化指示器<br>
     * 当没有配置时则不显示指示器
     */
    private void initIndicatorView() {
        if (null == mAdapter) {
            return;
        }
        AbstractIndicatorConfig indicatorConfig = mAdapter.getIndicatorConfig();
        if (null == indicatorConfig) {
            return;
        }
        if (indicatorConfig instanceof LineIndicatorConfig) {
            mIPageIndicator = new LineIndicatorView(getContext());
        } else if (indicatorConfig instanceof PointIndicatorConfig) {
            mIPageIndicator = new PointIndicatorView(getContext());
            ((PointIndicatorView) mIPageIndicator).addCallBack(this);
        } else if (indicatorConfig instanceof WrapIndicatorConfig) {
            mIPageIndicator = new WrapIndicatorView(getContext());
        }
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        if (indicatorConfig instanceof LineIndicatorConfig) {
            if (((LineIndicatorConfig) indicatorConfig).isTop()) {
                lp.gravity = Gravity.TOP;
            } else {
                lp.gravity = Gravity.BOTTOM;
            }
        } else if (indicatorConfig instanceof PointIndicatorConfig) {
            lp.gravity = Gravity.CENTER;
        }
        mIPageIndicator.init(indicatorConfig, mAdapter.getCount(), 0);
        mContentContainer.addView((View) mIPageIndicator, lp);
    }

    /**
     * 监听GlobalLayout以及绑定Vp
     */
    boolean hasIn;

    private void globalListenerAndBindVp() {
        if (!hasIn) {
            getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    hasIn = true;
                    bindViewPagerBySelf();
                }
            });
            return;
        }
        bindViewPagerBySelf();
    }

    /**
     * 绑定Vp
     */
    private void bindViewPagerBySelf() {
        if (null == mAdapter.mViewPager) {
            return;
        }
        if (mAdapter.mViewPager instanceof ViewPager) {
            ViewPager viewPager = (ViewPager) mAdapter.mViewPager;
            if (mHelper.getCurrentIndex() != viewPager.getCurrentItem()) {
                onPageSelected(viewPager.getCurrentItem());
            }
            bindViewPager();
        } else if (mAdapter.mViewPager instanceof ViewPager2) {
            ViewPager2 viewPager = (ViewPager2) mAdapter.mViewPager;
            int position = viewPager.getCurrentItem();
            if (mHelper.getCurrentIndex() != position) {
                onPageSelected(position);
                if (null != mIPageIndicator) {
                    mIPageIndicator.onPageSelected(position);
                }
            }
            bindViewPager2();
        }
    }

    /**
     * 绑定ViewPager
     *
     * @param viewPager
     */
    private ViewPager.OnPageChangeListener mOnPageChangeListener;

    private void bindViewPager() {
        if (null == mOnPageChangeListener) {
            mOnPageChangeListener = new ViewPager.OnPageChangeListener() {

                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    GodIndicator.this.onPageScrolled(position, positionOffset, positionOffsetPixels);
                }

                @Override
                public void onPageSelected(int position) {
                    GodIndicator.this.onPageSelected(position);
                }

                @Override
                public void onPageScrollStateChanged(int state) {
                    GodIndicator.this.onPageScrollStateChanged(state);
                }
            };
        }
        ViewPager viewPager = (ViewPager) mAdapter.mViewPager;
        viewPager.removeOnPageChangeListener(mOnPageChangeListener);
        viewPager.addOnPageChangeListener(mOnPageChangeListener);
    }

    /**
     * 绑定ViewPager2
     *
     * @param mViewPager2
     */
    private ViewPager2.OnPageChangeCallback mOnPageChangeCallback2;

    private void bindViewPager2() {
        if (null == mOnPageChangeCallback2) {
            mOnPageChangeCallback2 = new ViewPager2.OnPageChangeCallback() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    GodIndicator.this.onPageScrolled(position, positionOffset, positionOffsetPixels);
                }

                @Override
                public void onPageSelected(int position) {
                    GodIndicator.this.onPageSelected(position);
                }

                @Override
                public void onPageScrollStateChanged(int state) {
                    GodIndicator.this.onPageScrollStateChanged(state);
                }
            };
        }
        ViewPager2 viewPager = (ViewPager2) mAdapter.mViewPager;
        viewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback2);
        viewPager.registerOnPageChangeCallback(mOnPageChangeCallback2);
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (null == mAdapter) {
            return;
        }
        measureTitleViewPosition();
        if (null != mIPageIndicator) {
            mIPageIndicator.updateTitlePositions(mTitlePositions);
        }

        if (mHelper.getScrollState() == ScrollState.SCROLL_STATE_IDLE) {
            onPageSelected(mHelper.getCurrentIndex());
            onPageScrolled(mHelper.getCurrentIndex(), 0.0f, 0);
        }
    }

    /**
     * 测量标题坐标信息
     */
    private void measureTitleViewPosition() {
        if (null == mAdapter || null == mTitleContainer) {
            return;
        }
        int childCount = mTitleContainer.getChildCount();
        if (null == mTitlePositions) {
            mTitlePositions = new ArrayList<>(childCount);
        } else {
            mTitlePositions.clear();
        }
        for (int i = 0; i < childCount; i++) {
            View v = mTitleContainer.getChildAt(i);
            if (!(v instanceof IPageTitle)) {
                continue;
            }
            PositionInfo item = new PositionInfo();
            item.left = v.getLeft();
            item.right = v.getRight();
            item.top = v.getTop();
            item.bottom = v.getBottom();
            if (v instanceof MeasureTitleViewPosition) {
                final MeasureTitleViewPosition measure = (MeasureTitleViewPosition) v;
                item.contentLeft = measure.getContentLeft();
                item.contentRight = measure.getContentRight();
                item.contentTop = measure.getContentTop();
                item.contentBottom = measure.getContentBottom();
            } else {
                item.contentLeft = item.left;
                item.contentRight = item.right;
                item.contentTop = item.top;
                item.contentBottom = item.bottom;
            }
            mTitlePositions.add(item);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (null == mAdapter) {
            return;
        }
        mHelper.handlePageScrolled(position, positionOffset, positionOffsetPixels);
        if (!mGlobalConfig.isFollowFinger()) {
            return;
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }
        if (null == mTitlePositions || position >= mTitlePositions.size()) {
            return;
        }
        if (null == mScrollView || !mGlobalConfig.isCanOffScreen()) {
            return;
        }
        final int curIndex = Math.min(mTitlePositions.size() - 1, position);
        final int nextIndex = Math.min(mTitlePositions.size() - 1, position + 1);
        final PositionInfo curPosition = mTitlePositions.get(curIndex);
        final PositionInfo nextPosition = mTitlePositions.get(nextIndex);
        final float offset = mScrollView.getWidth() * mGlobalConfig.getPercentAfterScroll();
        final float preScroll = curPosition.horizontalCenter() - offset;
        final float nextScroll = nextPosition.horizontalCenter() - offset;
        final int distance = (int) (preScroll + (nextScroll - preScroll) * positionOffset);
        mScrollView.smoothScrollTo(distance, 0);
    }

    @Override
    public void onPageSelected(int position) {
        if (null == mAdapter) {
            return;
        }
        mHelper.handlePageSelected(position);
        if (mGlobalConfig.isFollowFinger()) {
            return;
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onPageSelected(position);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (null == mAdapter) {
            return;
        }
        mHelper.handlePageScrollStateChanged(state);
    }

    @Override
    public void onEntering(int index, int totalCount, float enterPercent, boolean leftToRight) {
//        Log.e(GodIndicator.class.getName() + this.hashCode(), "onEntering:" + index + " totalCount:" + totalCount + " enterPercent:" + enterPercent + " leftToRight:" + leftToRight);
        if (null == mAdapter) {
            return;
        }
        if (!mGlobalConfig.isFollowFinger()) {
            return;
        }
        if (null != mAdapter.getListener()) {
            mAdapter.getListener().onEntering(mAdapter, index, totalCount, enterPercent, leftToRight);
        }
        if (null != mTitleContainer) {
            View childAt = mTitleContainer.getChildAt(mHasMidDividerView ? 2 * index : index);
            if (childAt instanceof IPageTitle) {
                ((IPageTitle) childAt).onEntering(index, totalCount, enterPercent, leftToRight);
            }
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onEntering(index, totalCount, enterPercent, leftToRight);
        }
    }

    @Override
    public void onLeaving(int index, int totalCount, float leavePercent, boolean leftToRight) {
//        Log.e(GodIndicator.class.getName() + this.hashCode(), "onLeaving:" + index + " totalCount:" + totalCount + " leavePercent:" + leavePercent + " leftToRight:" + leftToRight);
        if (null == mAdapter) {
            return;
        }
        if (!mGlobalConfig.isFollowFinger()) {
            return;
        }
        if (null != mAdapter.getListener()) {
            mAdapter.getListener().onLeaving(mAdapter, index, totalCount, leavePercent, leftToRight);
        }
        if (null != mTitleContainer) {
            View childAt = mTitleContainer.getChildAt(mHasMidDividerView ? 2 * index : index);
            if (childAt instanceof IPageTitle) {
                ((IPageTitle) childAt).onLeaving(index, totalCount, leavePercent, leftToRight);
            }
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onLeaving(index, totalCount, leavePercent, leftToRight);
        }
    }

    @Override
    public void onSelected(int index, int totalCount) {
//        Log.e(GodIndicator.class.getName() + this.hashCode(), "onSelected:" + index + " totalCount:" + totalCount);
        if (null == mAdapter) {
            return;
        }
        if (null != mAdapter.getListener()) {
            mAdapter.getListener().onSelected(mAdapter, index, totalCount);
        }
        if (null != mTitleContainer) {
            View childAt = mTitleContainer.getChildAt(mHasMidDividerView ? 2 * index : index);
            if (childAt instanceof IPageTitle) {
                ((IPageTitle) childAt).onSelected(index, totalCount);
            }
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onSelected(index, totalCount);
        }
        if (null == mTitlePositions) {
            return;
        }
        //当不跟随手指移动时，选中改标题也需要滚动到指定位置
        if (mGlobalConfig.isCanOffScreen() && !mGlobalConfig.isFollowFinger() && mScrollView != null && mTitlePositions.size() > 0) {
            int currentIndex = Math.min(mTitlePositions.size() - 1, index);
            PositionInfo current = mTitlePositions.get(currentIndex);
            float scrollTo = current.horizontalCenter() - mScrollView.getWidth() * mGlobalConfig.getPercentAfterScroll();
            mScrollView.smoothScrollTo((int) (scrollTo), 0);
        }
    }

    @Override
    public void onCancelSelected(int index, int totalCount) {
//        Log.e(GodIndicator.class.getName() + this.hashCode(), "onCancelSelected:" + index + " totalCount:" + totalCount);
        if (null == mAdapter) {
            return;
        }
        if (null != mAdapter.getListener()) {
            mAdapter.getListener().onCancelSelected(mAdapter, index, totalCount);
        }
        if (null != mTitleContainer) {
            View childAt = mTitleContainer.getChildAt(mHasMidDividerView ? 2 * index : index);
            if (childAt instanceof IPageTitle) {
                ((IPageTitle) childAt).onCancelSelected(index, totalCount);
            }
        }
        if (null != mIPageIndicator) {
            mIPageIndicator.onCancelSelected(index, totalCount);
        }
    }

    /**
     * 返回适配器
     *
     * @return BaseIndicatorAdapter
     */
    public BaseIndicatorAdapter getAdapter() {
        return mAdapter;
    }

}
